fix(otel): scope deterministic ID generation - #627
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| String generateTraceIdForExecution(String arn, Instant executionStartTime) { | ||
| var timestamp = executionStartTime != null ? executionStartTime : Instant.now(); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment has been minimized.
This comment has been minimized.
| var timestamp = executionStartTime != null ? executionStartTime : Instant.now(); | ||
| var timestampHex = String.format("%08x", timestamp.getEpochSecond() & 0xffffffffL); | ||
| return timestampHex + sha256(arn != null ? arn : "").substring(0, 24); |
There was a problem hiding this comment.
Claude AI review
The Workflow trace ID's 8-hex timestamp prefix is derived from executionStartTime, and the Workflow span is exported only on the terminal invocation. AWS X-Ray rejects trace IDs whose epoch prefix falls outside its ingestion window (traces older than ~30 days, or in the future). Because durable executions can run far longer than that window (the SDK supports up to a year), a long-running execution will export its Workflow root with a start timestamp that is weeks or months in the past, so X-Ray silently drops it — the deterministic Workflow trace that is the entire point of this feature disappears for exactly the long-running case, while short executions look fine. The conformance suite only launches delays up to 86400s (~23h), so this gap is not caught in CI.
There is a genuine tension here: for the Workflow root to be a single logical trace across every invocation, the trace ID must be byte-identical each time, so the timestamp cannot simply be "now" — no timestamp is both stable across a 1-year execution and always inside X-Ray's window. The team should decide explicitly which property to sacrifice and document it: e.g. accept and document that Workflow-trace correlation in X-Ray only holds within the ingestion window, and/or fall back to a non-X-Ray-timestamped scheme when the execution age exceeds the window. At minimum, add a long-execution case (or a documented known-limitation note) so this failure mode is not silent.
Claude AI reviewThis PR is a well-structured, thoroughly tested refactor that fixes the real defect it targets: the base I found no confirmed correctness regression in the scoped-ID mechanism, the late-binding flow, or the parent-resolution changes. I raised one design-level correctness/observability risk inline (below). Residual test risk: The one open concern — the Workflow trace ID's timestamp prefix being the execution start time — is not exercised for genuinely long executions. The conformance workflow's Reviewed commit |
e6b5f7e to
1efff40
Compare
| * = "Workflow"}, {@code instrumentationName = "aws-durable-execution-sdk-java"}. A {@code null} passed to any builder | ||
| * setter falls back to the corresponding default. |
There was a problem hiding this comment.
Codex AI review
[P1] Preserve the public provider-selection API. This change removes ProviderSource, the provider/OTLP getters and builder methods, and providerSource() from both plugins. Existing clients will fail to compile or encounter linkage errors. Retain and deprecate these APIs while preserving AUTO_OTLP behavior until a major release.
| private final ThreadLocal<String> extractedTraceId = new ThreadLocal<>(); | ||
| private final ThreadLocal<String> arnDerivedTraceId = new ThreadLocal<>(); | ||
| private final ThreadLocal<String> pendingSpanOperationId = new ThreadLocal<>(); | ||
| private final ThreadLocal<String> pendingRawSpanId = new ThreadLocal<>(); | ||
| private final AtomicReference<String> durableExecutionArn = new AtomicReference<>(null); | ||
| private final ThreadLocal<IdOverride> scopedIds = new ThreadLocal<>(); | ||
| private final ThreadLocal<String> durableExecutionArn = new ThreadLocal<>(); |
There was a problem hiding this comment.
Codex AI review
[P2] Keep persistent setter state process-wide. These fields were AtomicReferences, so configuring the public generator once applied to spans created on executor threads. With ThreadLocal, those threads fall back to random trace IDs and an empty execution ARN, breaking cross-thread correlation. Use separate process-wide state for the legacy setters and reserve thread-local state for scoped overrides; add a cross-thread test.
| } | ||
|
|
||
| String generateTraceIdForExecution(String arn, Instant executionStartTime) { | ||
| var timestamp = executionStartTime != null ? executionStartTime : Instant.now(); |
There was a problem hiding this comment.
Codex AI review
[P2] Do not use wall-clock time as the missing-timestamp fallback. If executionStartTime is null, later invocations of the same durable execution generate different Workflow trace IDs and split the trace. Derive a stable fallback from the execution ARN, or reject the missing timestamp before emitting telemetry, and cover replay with a null timestamp.
Codex AI reviewFound three compatibility and deterministic-ID issues. Static review only; tests were not run per constraints. Reviewed commit |
Summary
AUTO_OTLPpipeline and redundantProviderSource; config/no-arg constructors use the global provider and builder constructors use caller-owned providersAUTO_OTLPTesting
mvn spotless:checkconformance-tests-otelagainst this PR's installed SDK and OTel plugin artifactsGeneral cloud tests were not run locally because they require deployed AWS resources. OpenTelemetry end-to-end behavior is covered by the conformance workflow merged in #630.
Closes #625